type classNames = string | string[];

type Element = ElementWrapper | HTMLElement;
type Props = string | Record<string, any>;


const StyleWithPX = {
    lineHeight: true,
    fontSize: true,
    width: true,
    height: true,
    top: true,
    left: true,
    right: true,
    bottom: true
}


function isElementWrapper(ele: Element) {
    return ele instanceof ElementWrapper;
}

function getNode(el: Element) {
    return isElementWrapper(el) ? (el as ElementWrapper).node() : el as HTMLElement;
}

function getStyleValue(key: string, value: any) {
    if (StyleWithPX[key] && typeof value === 'number') return value + 'px';
    return value;
}

function setElementAttribute(el: HTMLElement, key: string, value: any) {
    if (value == null) {
        el.removeAttribute(key);
    } else {
        el.setAttribute(key, value);
    }
}


class ElementWrapper{
    private el: HTMLElement;

    constructor(el: string | HTMLElement) {
        this.el = typeof el === 'string' ? document.createElement(el) : el;
    }

    addTo(el: Element) {
        getNode(el).appendChild(this.node());
        
        return this;
    }

    append(child: Element) {
        this.node().appendChild(getNode(child));
        
        return this;
    }

    text(): string;
    text(text: string): this;

    text(text?: string) {
        if (arguments.length === 0) return this.el.innerText;
        this.el.innerText = text;
        return this;
    }

    html(): string;
    html(html: string): this;
    html(innerHTML?: string) {
        if (arguments.length === 0) return this.node().innerHTML;
        this.node().innerHTML = innerHTML;

        return this;
    }

    remove() {
        const parent = this.el.parentNode as HTMLElement;
        parent && parent.removeChild(this.el);

        return this;
    }

    addClass(names: classNames) {
        if (Array.isArray(names)) {
            names.forEach(name => this.addClass(name));
        } else {
            if (this.el.className.indexOf(names) != -1) return this;

            this.el.className = this.el.className == "" ? names : `${this.el.className} ${names}`;
        }

        return this;
    }

    removeClass(name: classNames) {
        const classNames = this.el.className.split(' ');
        if (typeof name === 'string') {
            name = [name];
        }

        this.el.className = classNames.filter(cn => name.indexOf(cn) === -1).join(' ');

        return this;
    }

    css() {
        this.style.call(this, ...arguments);

        return this;
    }

    style(style: Props, value=null) {

        const el = this.node();
        if (typeof style === 'string') {
            el.style[style] = getStyleValue(style, value);
        } else {
            for (let key in style) {
                (<any>this.el.style)[key] = getStyleValue(key, style[key]);;
            }
        }

        return this;
    }

    attr(attr: Props, value=null) {
        const el = this.node();
        if (typeof attr === 'string') {
            setElementAttribute(el, attr, value);
        } else {
            for (let key in attr) {
                const value = attr[key];
                setElementAttribute(el, key, attr[key]);
            }
        }

        return this;
    }

    node() {
        return this.el;
    }

}

export default ElementWrapper;

